package org.carota.itext5;

import com.itextpdf.awt.geom.Rectangle2D;
import com.itextpdf.text.BadElementException;
import com.itextpdf.text.Image;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfSignatureAppearance;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.parser.ImageRenderInfo;
import com.itextpdf.text.pdf.parser.PdfReaderContentParser;
import com.itextpdf.text.pdf.parser.RenderListener;
import com.itextpdf.text.pdf.parser.TextRenderInfo;
import com.itextpdf.text.pdf.security.BouncyCastleDigest;
import com.itextpdf.text.pdf.security.DigestAlgorithms;
import com.itextpdf.text.pdf.security.MakeSignature;
import com.itextpdf.text.pdf.security.PrivateKeySignature;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.carota.pdf.PdfConfigurer;
import org.carota.pdf.PdfProcessor;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.Certificate;

/**
 * @author : AnWen
 * @version :1.0
 * @since : 2023-03-29 18:57
 */
@Slf4j
@NoArgsConstructor
public class SignatureConfigurer extends PdfConfigurer implements RenderListener {



    private FileInputStream p12;
    private char[] password;
    private Image chapterImg;

    private boolean append;
    private String reason;
    private String location;

    private Integer page;
    private String keyword;
    private com.itextpdf.awt.geom.Rectangle rectangle;

    public SignatureConfigurer(PdfProcessor processor) {
        super(processor);
        this.append = false;
        this.page = 1;
        this.rectangle = new com.itextpdf.awt.geom.Rectangle (0, 0, 150, 150);
    }

    public SignatureConfigurer p12(String p12Path, String password) {
        try {
            this.p12 = new FileInputStream (p12Path);
        } catch (FileNotFoundException e) {
            log.error ("初始化keystore stream 异常: ", e);
            throw new RuntimeException ("初始化keystore stream 异常");
        }
        this.password = password.toCharArray ();
        return this;
    }

    public SignatureConfigurer append(boolean append) {
        this.append = append;
        return this;
    }

    public SignatureConfigurer keyword(String keyword) {
        this.keyword = keyword;
        return this;
    }


    public SignatureConfigurer chapterImg(String chapterImg) {
        try {
            this.chapterImg = Image.getInstance (chapterImg);
        } catch (BadElementException | IOException e) {
            log.error ("初始化签章图片: ", e);
            throw new RuntimeException ("初始化签章图片");
        }
        return this;
    }

    public SignatureConfigurer reason(String reason) {
        this.reason = reason;
        return this;
    }

    public SignatureConfigurer location(String location) {
        this.location = location;
        return this;
    }

    public SignatureConfigurer page(int page) {
        this.page = page;
        return this;
    }

    private com.itextpdf.awt.geom.Rectangle getRectangle(PdfReader pdfReader) {
        try {
            PdfReaderContentParser pdfReaderContentParser = new PdfReaderContentParser (pdfReader);
            pdfReaderContentParser.processContent (page, this);
        } catch (IOException e) {
            log.error ("获取关键字坐标异常,使用默认坐标签章", e);
        }
        return rectangle;
    }


    /**
     * 电子签章
     */
    @Override
    public void execute() {
        PdfReader reader = processor.getPdfReader ();
        ByteArrayOutputStream bos = processor.getBos ();
        try {
            KeyStore pkcs12 = KeyStore.getInstance ("PKCS12");
            pkcs12.load (p12, password);
            String alias = pkcs12.aliases ().nextElement ();
            PrivateKey privateKey = (PrivateKey) pkcs12.getKey (alias, password);
            Certificate[] chain = pkcs12.getCertificateChain (alias);
            PdfStamper stamper = PdfStamper.createSignature (reader, bos, '\0', null, append);
            PdfSignatureAppearance appearance = stamper.getSignatureAppearance ();
            appearance.setReason (reason);
            appearance.setLocation (location);

            // 设置签名的位置，页码，签名域名称
            Rectangle rectangle = new Rectangle (getRectangle (reader));
            appearance.setVisibleSignature (rectangle, page, null);

            appearance.setSignatureGraphic (chapterImg);
            appearance.setCertificationLevel (append ? 1 : 0);
            appearance.setRenderingMode (PdfSignatureAppearance.RenderingMode.GRAPHIC);

            BouncyCastleDigest digest = new BouncyCastleDigest ();
            PrivateKeySignature signature = new PrivateKeySignature (privateKey, DigestAlgorithms.SHA256, null);
            MakeSignature.signDetached (appearance, digest, signature, chain, null, null, null, 0, MakeSignature.CryptoStandard.CMS);
        } catch (Exception e) {
            throw new RuntimeException ("添加签章失败",e);
        }
    }

    /**
     * 校验参数
     */
    @Override
    protected void checkParams() {
        checkParams("签章",p12, "p12", password, "password", chapterImg, "chapterImg");
    }

    @Override
    public int getOrder() {
        return 50;
    }


    @Override
    public void beginTextBlock() {

    }

    @Override
    public void renderText(TextRenderInfo renderInfo) {
        if (StringUtils.isNotBlank (keyword)) {
            String text = renderInfo.getText ();
            if (StringUtils.contains (text, keyword)) {
                Rectangle2D.Float boundingRectange = renderInfo.getBaseline ().getBoundingRectange ();
                double x = boundingRectange.getX ();
                double y = boundingRectange.getY ();
                rectangle = new com.itextpdf.awt.geom.Rectangle (x, y - 75, 150, 150);
            }
        }
    }


    @Override
    public void endTextBlock() {

    }

    @Override
    public void renderImage(ImageRenderInfo renderInfo) {

    }
}
